package mmc.resource.models.collada;

import java.util.ArrayList;
import java.util.List;

import org.collada.x2005.x11.colladaSchema.InputLocal;
import org.collada.x2005.x11.colladaSchema.InputLocalOffset;
import org.collada.x2005.x11.colladaSchema.SkinDocument.Skin;
import org.collada.x2005.x11.colladaSchema.SourceDocument.Source;

public class ColladaDataSource<T>{
	T[] data;
	String[] rawData;
	int count, stride;
	
	public ColladaDataSource(Skin parent, InputLocal in){
		this(resolve(parent, in));
	}
	
	public ColladaDataSource(Skin parent, InputLocalOffset in) {
		this(resolve(parent, in));
	}

	@SuppressWarnings("unchecked")
	public ColladaDataSource(Source s) {
		count = s.getTechniqueCommon().getAccessor().getCount().intValue();
		stride = s.getTechniqueCommon().getAccessor().getStride().intValue();
		
		Type<T> type = (Type<T>) Type.getByTypeString(s.getTechniqueCommon().getAccessor().getParamArray()[0].getType());

		rawData = type.getRawData(s).split("\\ ");
		
		if(count*stride!=rawData.length)
			throw new RuntimeException("internal error!");
		
		data = (T[]) new Object[count];
		for(int i=0;i<count;i++)
			data[i] = type.parseFrom(this, i);
		
		rawData = null;
	}

	public T get(int i){
		return data[i];
	}
	
	public int getCount(){
		return count;
	}
	
	static Source resolveSource(Skin skin, String sourceUrl){
		assert sourceUrl.charAt(0) == '#';
		sourceUrl = sourceUrl.substring(1);
		
		for(Source s : skin.getSourceArray()){
			if(s.getId().equals(sourceUrl))
				return s;
		}
		
		throw new RuntimeException("Invalid sourceUrl: "+sourceUrl);
	}
	
	public abstract static class Type<U>{
		public static Type<String> ID_REF = new Type<String>(){
			@Override
			public String parseFrom(ColladaDataSource<String> s, int id) {
				return s.rawData[id*s.stride];
			}

			@Override
			public String getRawData(Source s) {
				return s.getIDREFArray1().getStringValue();
			}};
		public static Type<Matrix4> FLOAT_4x4 = new Type<Matrix4>(){
			@Override
			public Matrix4 parseFrom(ColladaDataSource<Matrix4> s, int id) {
				List<Double> l = new ArrayList<Double>(16);
				for(int i=0;i<16;i++)
					l.add(Double.parseDouble(s.rawData[id*s.stride+i]));
				return new Matrix4(l);
			}

			@Override
			public String getRawData(Source s) {
				return s.getFloatArray1().getStringValue();
			}};
		public static Type<Float> FLOAT = new Type<Float>(){
			@Override
			public Float parseFrom(ColladaDataSource<Float> s, int id) {
				return Float.parseFloat(s.rawData[id*s.stride]);
			}

			@Override
			public String getRawData(Source s) {
				return s.getFloatArray1().getStringValue();
			}};
		
		public static Type<?> getByTypeString(String s){
			if(s.equals("IDREF")){
				return ID_REF;
			}else if(s.equals("float4x4")){
				return FLOAT_4x4;
			}else if(s.equals("float")){
				return FLOAT;
			}else{
				throw new RuntimeException("Invalid type "+s+"!");
			}
		}
			
		public abstract U parseFrom(ColladaDataSource<U> s, int id);
		public abstract String getRawData(Source s);
	}
	
	private static Source resolve(Skin parent, InputLocalOffset in) {
		return resolve(parent, in.getSource());
	}
	
	public static Source resolve(Skin parent, InputLocal in){
		return resolve(parent, in.getSource());
	}
	
	public static Source resolve(Skin skin, String sourceUrl){
		assert sourceUrl.charAt(0) == '#';
		sourceUrl = sourceUrl.substring(1);
		
		for(Source s : skin.getSourceArray()){
			if(s.getId().equals(sourceUrl))
				return s;
		}
		
		throw new RuntimeException("Invalid sourceUrl: "+sourceUrl);
	}
}